package com.hk.core.authentication.security.attempt;

import com.hk.commons.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
public class RedisUserAttemptServiceImpl implements UserAttemptService {

    private final String USER_ATTEMPT_PREFIX = "UserAttemptService_";

    private final StringRedisTemplate stringRedisTemplate;

    @Setter
    private int expireMinutes = 5;

    @Override
    public void updateFailAttempt(String account) {
        String key = USER_ATTEMPT_PREFIX + account;
        stringRedisTemplate.opsForValue().increment(key, 1);
        stringRedisTemplate.expire(key, expireMinutes, TimeUnit.MINUTES);
        RedisSerializer keySerializer = stringRedisTemplate.getKeySerializer();
        byte[] keyBytes = keySerializer.serialize(key);
        stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            connection.stringCommands().incrBy(keyBytes, 1);
            connection.keyCommands().expire(keyBytes, expireMinutes);
            return null;
        });
    }

    @Override
    public void resetFailAttempt(String account) {
        stringRedisTemplate.delete(account);
    }

    @Override
    public Optional<UserAttempt> getUserAttempt(String account) {
        String key = USER_ATTEMPT_PREFIX + account;
        String value = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.hasLength(value)) {
            long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
            int expireMinutes = expire <= 60 ? 1 : (int) (expire / 60);
            return Optional.of(new SimpleUserAttempt()
                    .setAttempt(Integer.parseInt(value))
                    .setUsername(account)
                    .setExpireMinutes(expireMinutes));
        }
        return Optional.empty();
    }
}
